package UIFree
{
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.Timer;

	public class FX extends EventDispatcher {
		private var _duration: int;
		private var _time: uint;
		private var _transition: Function;
		private var _set: Function;
		private var _delta: Number;
		protected var _updateTimer: Timer;
		
		public static var DEFAULTTRANSITION: Function = uif_transition.easeInOut(uif_transition.Default, 1);
		
		public function FX(a_set: Function, a_transition: Function=null, fps: int=16, duration: int=500) {
			super(null);
			_duration = duration;
			_updateTimer = new Timer(Math.round(1000 / fps));
			_updateTimer.addEventListener(TimerEvent.TIMER, doUpdateTimer);
			_set = a_set;
			_transition = (a_transition==null)?DEFAULTTRANSITION:a_transition;
		}
		
		public function get delta(): Number {
			return _delta;
		}
		
		public function get running(): Boolean {
			return _updateTimer.running;
		}
		
		protected function doStart(endFunc: Function = null, cancelFunc: Function = null): void {
			_updateTimer.start();
			_time		= (new Date()).time;
			dispatchEvent(new FXEvent(FXEvent.START, this));
			
			function eEventProc(e: FXEvent): void {
				if (endFunc != null) endFunc(e);
				removeEvents();
			};
			function eCancelProc(e: FXEvent): void {
				if (cancelFunc != null) cancelFunc(e);
				removeEvents();
			};
			function removeEvents(): void {
				removeEventListener(FXEvent.COMPLETE, eEventProc);
				removeEventListener(FXEvent.CANCEL, eCancelProc);
			}
			addEventListener(FXEvent.COMPLETE, eEventProc);
			addEventListener(FXEvent.CANCEL, eCancelProc);
			step();
		}
		
		protected function doCancel(): void {
			_updateTimer.stop();
			dispatchEvent(new FXEvent(FXEvent.CANCEL, this));
		}
		
		protected function doEnd(): void {
			dispatchEvent(new FXEvent(FXEvent.COMPLETE, this));
		}
		
		public function start(endFunc: Function = null, cancelFunc: Function = null): FX {
			if (!_updateTimer.running) doStart(endFunc, cancelFunc)
			return this;
		}
		
		public function cancel(): void {
			if (_updateTimer.running) doCancel();
		}
		
		protected function doUpdateTimer(e: TimerEvent): void {
			step();
		}
		
		protected function step(): void {
			var ltime: uint = (new Date()).time;
			_delta = this._transition((ltime - this._time) / this._duration);
			if (_delta > 1) _delta = 1;
			if (ltime < this._time + this._duration) {
				_set(_delta);
				dispatchEvent(new FXEvent(FXEvent.CONTINUE, this));
			} else {
				_set(1);
				_updateTimer.stop();
				doEnd();
			}
		}
		
		public function dispose(): void {
			_updateTimer.removeEventListener(TimerEvent.TIMER, doUpdateTimer);
		}

		protected static function compute(a_from: Number, a_to: Number, a_delta: Number): Number {
			return (a_to - a_from) * a_delta + a_from;
		}
	}
}